package com.abc.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.abc.config.MinioProperties;
import com.abc.exception.BusinessException;
import com.abc.exception.ErrorCode;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * MinIO工具类
 */
@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioProperties minioProperties;

    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return true存在，false不存在
     */
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("检查存储桶是否存在失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "检查存储桶失败");
        }
    }

    /**
     * 创建存储桶
     *
     * @param bucketName 存储桶名称
     */
    public void createBucket(String bucketName) {
        try {
            if (!bucketExists(bucketName)) {
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(bucketName)
                        .build());
                log.info("创建存储桶成功: {}", bucketName);
            }
        } catch (Exception e) {
            log.error("创建存储桶失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "创建存储桶失败");
        }
    }

    /**
     * 获取所有存储桶
     *
     * @return 存储桶列表
     */
    public List<Bucket> getAllBuckets() {
        try {
            return minioClient.listBuckets();
        } catch (Exception e) {
            log.error("获取存储桶列表失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "获取存储桶列表失败");
        }
    }

    /**
     * 删除存储桶
     *
     * @param bucketName 存储桶名称
     */
    public void removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            log.info("删除存储桶成功: {}", bucketName);
        } catch (Exception e) {
            log.error("删除存储桶失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "删除存储桶失败");
        }
    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @return 文件访问路径
     */
    public String uploadFile(MultipartFile file) {
        return uploadFile(file, minioProperties.getBucketName());
    }

    /**
     * 上传文件到指定存储桶
     *
     * @param file       文件
     * @param bucketName 存储桶名称
     * @return 文件访问路径
     */
    public String uploadFile(MultipartFile file, String bucketName) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAM_VALIDATION_FAILED, "文件不能为空");
        }

        try {
            // 确保存储桶存在
            createBucket(bucketName);

            // 生成文件名
            String fileName = generateFileName(file.getOriginalFilename());

            // 上传文件
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());

            log.info("文件上传成功: {}/{}", bucketName, fileName);

            return fileName;
        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 上传文件流
     *
     * @param inputStream 文件流
     * @param fileName    文件名
     * @param contentType 文件类型
     * @return 文件访问路径
     */
    public String uploadFile(InputStream inputStream, String fileName, String contentType) {
        return uploadFile(inputStream, fileName, contentType, minioProperties.getBucketName());
    }

    /**
     * 上传文件流到指定存储桶
     *
     * @param inputStream 文件流
     * @param fileName    文件名
     * @param contentType 文件类型
     * @param bucketName  存储桶名称
     * @return 文件访问路径
     */
    public String uploadFile(InputStream inputStream, String fileName, String contentType, String bucketName) {
        try {
            // 确保存储桶存在
            createBucket(bucketName);

            // 生成文件名
            String generatedFileName = generateFileName(fileName);

            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(generatedFileName)
                    .stream(inputStream, -1, 10485760) // 10MB
                    .contentType(contentType)
                    .build());

            log.info("文件流上传成功: {}/{}", bucketName, generatedFileName);

            return generatedFileName;
        } catch (Exception e) {
            log.error("文件流上传失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "文件流上传失败: " + e.getMessage());
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名
     */
    public void removeFile(String fileName) {
        removeFile(fileName, minioProperties.getBucketName());
    }

    /**
     * 删除指定存储桶中的文件
     *
     * @param fileName   文件名
     * @param bucketName 存储桶名称
     */
    public void removeFile(String fileName, String bucketName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build());
            log.info("删除文件成功: {}/{}", bucketName, fileName);
        } catch (Exception e) {
            log.error("删除文件失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "删除文件失败");
        }
    }

    /**
     * 获取文件流
     *
     * @param fileName 文件名
     * @return 文件流
     */
    public InputStream getFileStream(String fileName) {
        return getFileStream(fileName, minioProperties.getBucketName());
    }

    /**
     * 获取指定存储桶中的文件流
     *
     * @param fileName   文件名
     * @param bucketName 存储桶名称
     * @return 文件流
     */
    public InputStream getFileStream(String fileName, String bucketName) {
        try {
            return minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build());
        } catch (Exception e) {
            log.error("获取文件流失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "获取文件流失败");
        }
    }

    /**
     * 获取文件访问URL
     *
     * @param fileName 文件名
     * @return 文件访问URL
     */
    public String getFileUrl(String fileName) {
        return getFileUrl(fileName, minioProperties.getBucketName());
    }

    /**
     * 获取指定存储桶中文件的访问URL
     *
     * @param fileName   文件名
     * @param bucketName 存储桶名称
     * @return 文件访问URL
     */
    public String getFileUrl(String fileName, String bucketName) {
        return minioProperties.getUrlPrefix() + "/" + bucketName + "/" + fileName;
    }

    /**
     * 获取预签名的文件访问URL（临时访问链接）
     *
     * @param fileName 文件名
     * @param expires  过期时间（秒）
     * @return 预签名URL
     */
    public String getPresignedFileUrl(String fileName, int expires) {
        return getPresignedFileUrl(fileName, minioProperties.getBucketName(), expires);
    }

    /**
     * 获取预签名的文件访问URL（临时访问链接）
     *
     * @param fileName   文件名
     * @param bucketName 存储桶名称
     * @param expires    过期时间（秒）
     * @return 预签名URL
     */
    public String getPresignedFileUrl(String fileName, String bucketName, int expires) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(fileName)
                    .expiry(expires)
                    .build());
        } catch (Exception e) {
            log.error("获取预签名URL失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "获取预签名URL失败");
        }
    }

    /**
     * 列出存储桶中的所有文件
     *
     * @param bucketName 存储桶名称
     * @return 文件列表
     */
    public Iterable<Result<Item>> listFiles(String bucketName) {
        try {
            return minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("列出文件失败: {}", e.getMessage());
            throw new BusinessException(ErrorCode.INTERNAL_ERROR, "列出文件失败");
        }
    }

    /**
     * 生成文件存储路径（保留原始文件名）
     *
     * @param originalFileName 原始文件名
     * @return 生成的文件路径
     */
    private String generateFileName(String originalFileName) {
        if (StrUtil.isBlank(originalFileName)) {
            originalFileName = "unknown";
        }

        // 生成日期文件夹和保留原始文件名
        String dateFolder = DateUtil.format(new Date(), "yyyy/MM/dd");
        
        // 清理文件名，移除特殊字符，避免路径问题
        String cleanFileName = originalFileName.replaceAll("[^a-zA-Z0-9\u4e00-\u9fa5._-]", "_");
        
        return dateFolder + "/" + cleanFileName;
    }
}
